#pragma once
#include <iostream>

#include <cerrno>
#include <cstring>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "Log.hpp"
#include "sockaddr.hpp"
#include "Comm.hpp"

#define DEFAULT_SOCKFD -1
#define READ_BUFFER_SIZE 1024
#define RECONNECT_SIZE 5

int reconnect_size = 1;

class Client
{
public:
    Client(std::string ip, in_port_t port)
        : _sockfd(DEFAULT_SOCKFD), _sk(ip, port)
    {
    }
    ~Client()
    {
    }
    void Init()
    {
        //创建套接字
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        reconnect(_sockfd, func_types[0]); // 失败重连
        CheckRet(_sockfd, client_name, func_types[0]); //保证成功才调用，只是打印成功的信息

        // 请求连接
        int ret_connect = connect(_sockfd, (const sockaddr *)(&_sk.addr_in()), _sk.len());
        reconnect(ret_connect, func_types[6]); // 失败重连
        CheckRet(ret_connect, client_name, func_types[6]);

        // 重连成功
        reconnect_size = 1;
    }
    void Start()
    {
        char read_buffer[READ_BUFFER_SIZE];
        
        while (true)
        {
            std::string write_buffer;
            std::cout << "Client send# ";
            std::getline(std::cin, write_buffer);

            if(write_buffer == "quit") break;

            //发送信息
            int ret_write = write(_sockfd, write_buffer.c_str(), write_buffer.size());
            reconnect(ret_write, func_types[5]); // 失败重连
            CheckRet(ret_write, client_name, func_types[5]);

            //接收server的响应
            int ret_read = read(_sockfd, read_buffer, sizeof(read_buffer) - 1);
            reconnect(ret_read, func_types[4]); // 失败重连
            CheckRet(ret_read, client_name, func_types[4]);

            read_buffer[ret_read] = 0;
            std::cout << read_buffer << std::endl;
        }

        close(_sockfd);
    }

private:
    int _sockfd;
    Sockaddr _sk;

    // 重新连接
    void reconnect(int ret, std::string err)
    {
        if(ret == 0 && (err == "Write" || err == "Read") ) ret = -1; //啥也不写
        if (ret < 0)
        {
            close(_sockfd);
            //将失败的函数和原因打印出来
            std::cout << "["<<  err << " error]# " << "errno -> " << errno << ", error -> " << strerror(errno) << std::endl;

            while (reconnect_size <= RECONNECT_SIZE)
            {
                sleep(1);
                std::cout << "reconnecting ... " << reconnect_size++ << std::endl;
                Init();

                // 重连成功
                std::cout << "reconnect success!" << std::endl;
                Start();
            }

            std::cout << "reconnect fail" << std::endl;
            exit(-1);
        }

    }

};